#ifndef _core_dispatch_detail_invoker_impl_hpp_
#define _core_dispatch_detail_invoker_impl_hpp_

/** \file invoker_impl.hpp
 *  \author Adrian Schweizer
 *  \created  $Sa 18 Aug 03:48:01 pm CEST 2007 schwadri@SchwadriComp.local$
 *  \modified $So 19 Aug 03:47:32 pm CEST 2007 schwadri@SchwadriComp.local$
 */
#include "../limits.hpp"

#include <boost/type_traits/function_traits.hpp>

#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/arithmetic/add.hpp>
#include <boost/preprocessor/repetition/enum.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/iteration/local.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>



namespace core {

    namespace dispatch {

        namespace detail {
            using boost::function_traits;

            //generate invoker implementations for 0 up to CORE_LIMIT_INVOKER_MAX_ARITY arguments

            #define CORE_GENERATE_PARAMS(z,n,type)        BOOST_PP_CAT(type,n)
            #define CORE_GENERATE_ARGS(z,n,type)          BOOST_PP_CAT(A,n) BOOST_PP_CAT(arg,n)
            #define CORE_GENERATE_SAVE_TEMP(z,n,type)     BOOST_PP_CAT(BOOST_PP_CAT(m_,type),n) = BOOST_PP_CAT(type,n);
            #define CORE_GENERATE_TEMP(z,n,type)          BOOST_PP_CAT(A,n) BOOST_PP_CAT(BOOST_PP_CAT(m_,type),n);

			#ifdef _MSC_VER
				#define CORE_TEMPL_HINT
			#else
				#define CORE_TEMPL_HINT template
			#endif

            #define CORE_GENERATE_INVOKER_IMPLS(n)                                      \
            template<typename R, typename F, typename Key, typename TypeLookup BOOST_PP_COMMA_IF(n)\
                BOOST_PP_ENUM(n,CORE_GENERATE_PARAMS,typename A)>                       \
                struct BOOST_PP_CAT(invoker_impl_,n)                                    \
                {                                                                       \
                    typedef R return_type;                                              \
                    BOOST_PP_CAT(invoker_impl_,n) (Key k, F f)                          \
                    : m_key(k), m_f(f)                                                  \
                    { }                                                                 \
                private:                                                                \
                    Key m_key;                                                          \
                    F   m_f;                                                            \
                    TypeLookup m_lookup;                                                \
                public:                                                                 \
                    R operator()(BOOST_PP_ENUM(n,CORE_GENERATE_ARGS,))                  \
                    {                                                                   \
                        BOOST_PP_REPEAT(n,CORE_GENERATE_SAVE_TEMP,arg)                  \
                        return m_lookup.type_lookup(m_key,*this);                       \
                    }                                                                   \
                template<class Action>                                                  \
                    R invoke()                                                          \
                    {                                                                   \
                        return m_f.CORE_TEMPL_HINT operator()<Action>(BOOST_PP_ENUM(n,CORE_GENERATE_PARAMS,m_arg));\
                    }                                                                   \
                private:                                                                \
                    BOOST_PP_REPEAT(n,CORE_GENERATE_TEMP,arg)                           \
                };                                                                      \
                                                                                        \
            template<typename F, typename Key, typename TypeLookup BOOST_PP_COMMA_IF(n)\
                BOOST_PP_ENUM(n,CORE_GENERATE_PARAMS,typename A)>                       \
                struct BOOST_PP_CAT(invoker_impl_,n)<void,F,Key,TypeLookup              \
                BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM(n,CORE_GENERATE_PARAMS,A)>           \
                {                                                                       \
                    typedef void return_type;                                           \
                    BOOST_PP_CAT(invoker_impl_,n) (Key k, F f)                          \
                    : m_key(k), m_f(f)                                                  \
                    { }                                                                 \
                private:                                                                \
                    Key m_key;                                                          \
                    F   m_f;                                                            \
                    TypeLookup m_lookup;                                                \
                public:                                                                 \
                    void operator()(BOOST_PP_ENUM(n,CORE_GENERATE_ARGS,))               \
                    {                                                                   \
                        BOOST_PP_REPEAT(n,CORE_GENERATE_SAVE_TEMP,arg)                  \
                        m_lookup.type_lookup(m_key,*this);                              \
                    }                                                                   \
                template<class Action>                                                  \
                    void invoke()                                                       \
                    {                                                                   \
                        m_f.CORE_TEMPL_HINT operator()<Action>(BOOST_PP_ENUM(n,CORE_GENERATE_PARAMS,m_arg));\
                    }                                                                   \
                private:                                                                \
                    BOOST_PP_REPEAT(n,CORE_GENERATE_TEMP,arg)                           \
                };

            template <int ArgumentCount, typename Signature, typename Functor, class Key, class TypeLookup>
                class get_invoker_impl;

            #define CORE_GET_INVOKER_ARG_TYPE(z,n,val)  BOOST_PP_CAT(typename traits::arg,BOOST_PP_CAT(BOOST_PP_ADD(n,1),_type))

            #define CORE_GENERATE_GET_INVOKER_IMPL(n)                                   \
            template <typename Signature, typename Functor, class Key, class TypeLookup>\
                class get_invoker_impl<n, Signature, Functor, Key, TypeLookup>          \
                {                                                                       \
                    typedef function_traits<Signature> traits;                          \
                public:                                                                 \
                    typedef BOOST_PP_CAT(invoker_impl_,n)<                              \
                                typename traits::result_type,                           \
                                Functor,                                                \
                                Key,                                                    \
                                TypeLookup BOOST_PP_COMMA_IF(n)                         \
                                BOOST_PP_ENUM(n,CORE_GET_INVOKER_ARG_TYPE, )> type;     \
                };
            #define CORE_GENERATE_INVOKERS(n)       \
                    CORE_GENERATE_INVOKER_IMPLS(n)  \
                    CORE_GENERATE_GET_INVOKER_IMPL(n)

            #define BOOST_PP_LOCAL_MACRO   CORE_GENERATE_INVOKERS
            #define BOOST_PP_LOCAL_LIMITS (0, CORE_LIMIT_INVOKER_MAX_ARITY)
            //DO NOT REMOVE WHITESPACE   ^
            #include BOOST_PP_LOCAL_ITERATE()

            #undef CORE_GENERATE_INVOKERS
            #undef CORE_GENERATE_GET_INVOKER_IMPL
            #undef CORE_GET_INVOKER_ARG_TYPE
            #undef CORE_GENERATE_INVOKER_IMPLS
            #undef CORE_GENERATE_TEMP
            #undef CORE_GENERATE_SAVE_TEMP
            #undef CORE_GENERATE_ARGS
            #undef CORE_GENERATE_PARAMS
			#undef CORE_TEMPL_HINT
        }

    } // namespace dispatch

} // namespace core


#endif // _core_dispatch_detail_invoker_impl_hpp_
